package com.example.mq.mqclient;

import com.example.mq.common.*;
import com.example.mq.mqsever.core.BasicProperties;
import com.example.mq.mqsever.core.Exchangetype;
import org.apache.logging.log4j.message.ReusableMessage;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
import sun.text.bidi.BidiBase;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-09-08
 * Time: 13:40
 */
public class Channel {
    private  String channelId;
    //当前channel属于哪个连接
    private  Connection connection;

    private ConcurrentHashMap<String, BasicReturns> basicReturnsMap=new ConcurrentHashMap<>();
    //如果当前Channel订阅了某个队列,就需要在此处记录下对应的回调是啥,当该队列的消息返回回来的时候,调用回调
    //此处约定一个Channel中只有一个回调
    private Consumer consumer=null;

    public Channel(String channelId,Connection connection){
        this.channelId=channelId;
        this.connection=connection;
    }

    public String getChannelId() {
        return channelId;
    }

    public void setChannelId(String channelId) {
        this.channelId = channelId;
    }

    public Connection getConnection() {
        return connection;
    }

    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    public ConcurrentHashMap<String, BasicReturns> getBasicReturnsMap() {
        return basicReturnsMap;
    }

    public void setBasicReturnsMap(ConcurrentHashMap<String, BasicReturns> basicReturnsMap) {
        this.basicReturnsMap = basicReturnsMap;
    }

    public Consumer getConsumer() {
        return consumer;
    }

    public void setConsumer(Consumer consumer) {
        this.consumer = consumer;
    }

    //在这个方法中,和服务器进行交互,告诉服务器说此处创建了新的channel
    public boolean createChannel() throws IOException {
        //对于创建Channel操作来说,payload就是一个basicArguments对象
        BasicArguments basicArguments=new BasicArguments();
        basicArguments.setChannelId(channelId);
        basicArguments.setRid(generateRid());
        byte[] payload= BinaryTool.toBytes(basicArguments);


        Request request=new Request();
        //创建channel
        request.setType(0x1);
        request.setLength(payload.length);
        request.setPayload(payload);

        //构造出完整请求之后,就可以发送这个请求了

        connection.writeRequest(request);

        //等待服务器的响应
        BasicReturns basicReturns=waitResult(basicArguments.getRid());



        return true;
    }

    //这个方法用来阻塞等待服务器的响应
    private BasicReturns waitResult(String rid) {
        BasicReturns basicReturns=null;
        while((basicReturns=basicReturnsMap.get(rid))==null){
            //如果查询结果为null,说明包裹还没回来
            //此时就需要阻塞等待
            synchronized (this){
                try{
                    wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }

        }
        //读取成功之后,还需要把这个消息从哈希表中删除
        basicReturnsMap.remove(rid);
        return basicReturns;
    }

    public void putReturns(BasicReturns basicReturns) {
        basicReturnsMap.put(basicReturns.getRid(),basicReturns);
        synchronized (this){
            //当前不知道有多少个线程在等响应
            //把所有的线程都唤醒
            notifyAll();
        }
    }


    private String generateRid(){
        return "R-"+ UUID.randomUUID().toString();
    }

    //关闭channel,给服务器发一个type=0x2的请求
    public boolean close() throws IOException {
        BasicArguments basicArguments=new BasicArguments();
        basicArguments.setRid(generateRid());
        basicArguments.setChannelId(channelId);
        byte[] payload=BinaryTool.toBytes(basicArguments);


        Request request=new Request();
        request.setType(0X2);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);
        BasicReturns basicReturns=waitResult(basicArguments.getRid());
        return basicReturns.isOk();
    }

    //创建交换机
    public boolean exchangeDeclare(String exchangeName, Exchangetype exchangetype,boolean durable,boolean autoDelete,
                                   Map<String,Object> arguments) throws IOException {
            ExchangeDeclareArguments exchangeDeclareArguments=new ExchangeDeclareArguments();
            exchangeDeclareArguments.setRid(generateRid());
            exchangeDeclareArguments.setChannelId(channelId);
            exchangeDeclareArguments.setExchangeName(exchangeName);
            exchangeDeclareArguments.setExchangeType(exchangetype);
            exchangeDeclareArguments.setDurable(durable);
            exchangeDeclareArguments.setAutoDelete(autoDelete);
            exchangeDeclareArguments.setArguments(arguments);
            byte[] payload=BinaryTool.toBytes(exchangeDeclareArguments);



            Request request=new Request();
            request.setType(0x3);
            request.setLength(payload.length);
            request.setPayload(payload);

            connection.writeRequest(request);
            BasicReturns basicReturns=waitResult(exchangeDeclareArguments.getRid());

            return  basicReturns.isOk();


    }

    //删除交换机
    public boolean exchangeDelete(String exchangeName) throws IOException {
        ExchangeDeleteArguments deleteArguments=new ExchangeDeleteArguments();
        deleteArguments.setRid(generateRid());
        deleteArguments.setChannelId(channelId);
        deleteArguments.setExchangeName(exchangeName);
        byte[] payload=BinaryTool.toBytes(deleteArguments);

        Request request=new Request();
        request.setType(0x4);
        request.setLength(payload.length);
        request.setPayload(payload);


        connection.writeRequest(request);
        BasicReturns basicReturns=waitResult(deleteArguments.getRid());
        return basicReturns.isOk();
    }
//创建队列
    public boolean queueDeclare(String queueName,boolean durable,boolean autoDelete,boolean exclusive
                                ,Map<String,Object> arguments) throws IOException {
        QueueDeclareArguments queueDeclareArguments=new QueueDeclareArguments();
        queueDeclareArguments.setRid(generateRid());
        queueDeclareArguments.setChannelId(channelId);
        queueDeclareArguments.setQueueName(queueName);
        queueDeclareArguments.setDurable(durable);
        queueDeclareArguments.setExclusive(exclusive);
        queueDeclareArguments.setAutoDelete(autoDelete);
        queueDeclareArguments.setArguments(arguments);
        byte[] payload=BinaryTool.toBytes(queueDeclareArguments);



        Request request=new Request();
        request.setType(0x5);
        request.setLength(payload.length);
        request.setPayload(payload);


        connection.writeRequest(request);
        BasicReturns basicReturns=waitResult(queueDeclareArguments.getRid());
        return basicReturns.isOk();
    }


    //删除队列
    public boolean queueDelete(String queueName) throws IOException {
            QueueDeleteArguments arguments=new QueueDeleteArguments();
            arguments.setRid(generateRid());
            arguments.setChannelId(channelId);
            arguments.setQueueName(queueName);
            byte[] payload= BinaryTool.toBytes(arguments);


            Request request=new Request();
            request.setType(0x6);
            request.setLength(payload.length);
            request.setPayload(payload);


            connection.writeRequest(request);
            BasicReturns basicReturns=waitResult(arguments.getRid());
            return basicReturns.isOk();
    }


    //创建绑定
    public boolean queueBind(String queueName,String exchangeName,String bindingKey) throws IOException {
        QueueBindArguments arguments=new QueueBindArguments();
        arguments.setRid(generateRid());
        arguments.setQueueName(queueName);
        arguments.setExchangeName(exchangeName);
        arguments.setChannelId(channelId);
        arguments.setBindingKey(bindingKey);
        byte[] payload=BinaryTool.toBytes(arguments);

        Request request=new Request();
        request.setType(0x7);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);
        BasicReturns basicReturns=waitResult(arguments.getRid());
        return basicReturns.isOk();
    }
//解除绑定

    public boolean queueUnBind(String queueName,String exchangeName) throws IOException {
        QueueUnBindArguments arguments=new QueueUnBindArguments();
        arguments.setRid(generateRid());
        arguments.setChannelId(channelId);
        arguments.setQueueName(queueName);
        arguments.setExchangeName(exchangeName);
        byte[] payload=BinaryTool.toBytes(arguments);


        Request request=new Request();
        request.setType(0x8);
        request.setLength(payload.length);
        request.setPayload(payload);


        connection.writeRequest(request);
        BasicReturns basicReturns=waitResult(arguments.getRid());
        return basicReturns.isOk();
    }


    //发送消息
    public boolean basicPublish(String exchangeName, String routingKey, BasicProperties basicProperties,byte[] body) throws IOException {
        BasicPublishArguments arguments=new BasicPublishArguments();
        arguments.setRid(generateRid());
        arguments.setChannelId(channelId);
        arguments.setExchangeName(exchangeName);
        arguments.setRoutingKey(routingKey);
        arguments.setBasicProperties(basicProperties);
        arguments.setBody(body);
        byte[] payload=BinaryTool.toBytes(arguments);

        Request request=new Request();
        request.setType(0x9);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);
        BasicReturns basicReturns=waitResult(arguments.getRid());
        return basicReturns.isOk();
    }

    //订阅消息
    public boolean basicConsume(String queueName,boolean autoAck,Consumer consumer) throws MqException, IOException {
        //设置回调
        if(this.consumer!=null){
            throw  new MqException("该channel已经设置过消费消息的回调了,不能再重复设置!");
        }
        this.consumer=consumer;


        BasicConsumeArguments arguments=new BasicConsumeArguments();
        arguments.setRid(generateRid());
        arguments.setChannelId(channelId);
        arguments.setConsumeTag(channelId);//此处的consumerTag就用channelid表示了
        arguments.setQueueName(queueName);
        arguments.setAutoAck(autoAck);
        byte[] payload=BinaryTool.toBytes(arguments);

        Request request=new Request();
        request.setType(0xa);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);
        BasicReturns basicReturns=waitResult(arguments.getRid());
        return basicReturns.isOk();
    }

    //确认消息
    public boolean basicAck(String queueName,String messageId) throws IOException {
    BasicAckArguments arguments=new BasicAckArguments();
    arguments.setRid(generateRid());
    arguments.setChannelId(channelId);
    arguments.setQueueName(queueName);
    arguments.setMessageId(messageId);
    byte[] payload=BinaryTool.toBytes(arguments);

        Request request=new Request();
        request.setType(0xb);
        request.setLength(payload.length);
        request.setPayload(payload);


        connection.writeRequest(request);
        BasicReturns basicReturns=waitResult(arguments.getRid());
        return basicReturns.isOk();

    }


}
